import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Execution extends JPanel implements Runnable {
	
	private Vector<Dessinable> dessins;
	public Point startingPoint;
	//public Point modifier;
	//public double orientation;
	public int pas;
	public float epaisseur;
	public boolean dessine;
	public boolean affiche;
	public boolean quadrillage;
	public Color couleur;
	public Tortue turtle;
	//buffer capture d'ecran
	private BufferedImage capture;

	
	public int zoom = 1;
	public int vitesseZoom = 2;
	
	public float decalageX = 0;
	public float decalageY = 0;
//	private float decalageXInitial;
//	private float decalageYInitial;
	private float decalageXSupplementaire;
	private float decalageYSupplementaire;
	
	public Execution()
	{
		super();
//		t = new Thread(this);
		PileDeplacement = new Vector();
		this.dessins = new Vector<Dessinable>();
		this.setBackground(Color.WHITE);
		this.clearScreen();		
	}	

	public Dimension getPreferredSize() {
		return new Dimension(1000,1000);
	}
	

	public void init()
	{
		int y = 200;
		int x = 250;
		ppx = 250;
		ppy = 200;
		this.startingPoint 	= new Point(x,y);
		this.orientation 	= 0;
		this.affiche		= true;
		this.quadrillage	= true;
		this.pas 			= 10;
		this.epaisseur		= 1.0f;
		this.dessine		= true;
		this.couleur		= Color.blue;
		turtle = new Tortue(startingPoint,orientation,affiche);
		this.ajouterDessinable(turtle);
		repaint();
	}
	
	public void clearScreen()
	{
		dessins.clear();
		this.init();	
	}
	
	
	//---------------------------------------------------------------
	/* Methode pour ajouter un dessin � la liste des dessins a afficher */
	public synchronized void ajouterDessinable(Dessinable d){		
		dessins.add(d);
		}
	
	/* Methode pour remplacer un dessin dans la liste des dessins a afficher */
	public void remplacerDessinable(Dessinable d, int pos)
	{
		boolean trouve = false;
		for (int i =0;i<dessins.size();i++)
		{
			if (dessins.get(i) instanceof Tortue)
			{
				dessins.removeElementAt(i);
				dessins.insertElementAt(d,dessins.size());
				trouve = true;
			}
		} 
		if (!trouve) {
			Tortue t = new Tortue(getPoint(),getOrientation(),true);
			dessins.insertElementAt(d,dessins.size());
		}
	}
	
	//enregistrer le panneau
	private String currentFile;
	private File currentPath = (new JFileChooser()).getCurrentDirectory();
	private FiltreFichier FFPng = new FiltreFichier( new String[]{"png"},"Image PNG : *.png");
	public void Enregistrer()
	{
		Rectangle zone = new Rectangle(0,0,this.getSize().width,this.getSize().height);
		try{
		capture = new Robot().createScreenCapture(zone);
		//recuperer les elements graphiques du panneau 
		Graphics g = capture.getGraphics();
		this.paint(g);
		}
		catch(AWTException e){
			e.printStackTrace();
		}
		
		JFileChooser choix = new JFileChooser();
		choix.setCurrentDirectory(currentPath);
		choix.addChoosableFileFilter(FFPng);
		int retour = choix.showSaveDialog(getParent());
		if(retour == JFileChooser.APPROVE_OPTION) {			
			String extensionSelectionner = choix.getFileFilter().getDescription().substring(choix.getFileFilter().getDescription().indexOf("*")+1,choix.getFileFilter().getDescription().length());
			currentFile = choix.getSelectedFile().getAbsolutePath(); //sauvegarde du nom fichier			
			if(choix.getSelectedFile().toString().lastIndexOf(".")>0)
			{					
				if(!choix.getSelectedFile().toString().substring(
							choix.getSelectedFile().toString().lastIndexOf("."),
							choix.getSelectedFile().toString().length()).equals(extensionSelectionner))
					{currentFile+=extensionSelectionner;}
			}
			else {currentFile+=extensionSelectionner;}
			currentPath = choix.getSelectedFile().getAbsoluteFile();
			choix.setCurrentDirectory(currentPath.getParentFile());
			try{ImageIO.write(capture, "PNG", new File(currentFile));}
			catch(IOException e){e.printStackTrace();}
		}				
	}	
	
	
	public void paintComponent(Graphics g)
	{
		((Graphics2D)g).scale(zoom, zoom) ;
		((Graphics2D)g).translate(decalageX+decalageXSupplementaire,decalageY+decalageYSupplementaire);
		
		super.paintComponent(g);
		
		//quadrillage
		if (quadrillage)
		{
			g.setColor(new Color(230,230,230));
			for(int i=0;i<this.getWidth();i+=10){
				g.drawLine(i,0,i,this.getHeight());
			}
			
			for(int i=0;i<this.getHeight();i+=10){
				g.drawLine(0,i,this.getWidth(),i);
			}
		}
		
		for (int i =0;i<dessins.size();i++)
		{
			dessins.get(i).dessiner((Graphics2D) g);
		}
		
		
	}
	
	public void setPoint(Point p){this.startingPoint = p;}
	public Point getPoint(){return this.startingPoint;}
	public double getOrientation(){return turtle.orientation; }
	public void setOrientation(double o){turtle.orientation = o; this.repaint();}
	public int getPas() {return this.pas;}
	public void setPas(int p) {this.pas = p;}
	public float getEpaisseur() {return this.epaisseur;}
	public void setEpaisseur(float e) {this.epaisseur = e;}
	public boolean getDessine() {return this.dessine;}
	public void setDessine(boolean d) {this.dessine = d;}
	public Color getCouleur() {return this.couleur;}
	public void setCouleur(Color c) {this.couleur = c;}
	public Color getFond() {return this.getBackground();}
	public void setFond(Color c) {this.setBackground(c);}
	public synchronized void setTortue(Point p, double cap,boolean affiche) 
	{
		Tortue t = new Tortue(p,cap,affiche);
		if (affiche) {
			this.remplacerDessinable(t,0);
			this.repaint();
		}
		else {
			for (int i =0;i<dessins.size();i++)
			{
				if (dessins.get(i) instanceof Tortue)
				{
					dessins.removeElementAt(i);
				}
			} 
		}
		this.repaint();
	}
	public Tortue getTortue(){return turtle;}	
	public boolean getQuadrillage(){return quadrillage; }
	public void setQuadrillage(boolean  quadrillage){ 
		this.quadrillage=quadrillage;
		this.repaint();
	}	
	public boolean getAffiche(){return turtle.getAffiche(); }
	public void setAffiche(boolean  affiche){ 
		turtle.setAffiche(affiche);
		this.repaint();
	}
	private boolean changeVitesse = false;
	public void setVitesse(int vit){
		changeVitesse = true;
		this.vitesse=vit;
	}
	
	
	/********FONCTION DE DEPLACEMENT********/
	private double ppx = 250;
	private double ppy = 200;
	public void setPpx(double x) {ppx = x;}
	public void setPpy(double y) {ppy = y;}
	
	public Point Calcul(int distance, double orientation, Point start, boolean sens)
	{
		Point p;
		if (sens)
		{
			ppx =  (distance * (java.lang.Math.cos(orientation))) + ppx;
			ppy =  (distance * (java.lang.Math.sin(orientation))) + ppy;
		}
		else 
		{
			ppx =  (-1*distance * (java.lang.Math.cos(orientation))) + ppx;
			ppy =  (-1*distance * (java.lang.Math.sin(orientation))) + ppy;
		}
		p = new Point((int)ppx,(int)ppy);

		return p;
	}
	private Point p1,p2;
	private double orientation;
	private Thread t;
	public Vector<MessagePile> PileDeplacement;
	public boolean enDeplacement() {return !PileDeplacement.isEmpty();} //renvoie vrai si il ya un deplacement en cours
	public Vector<MessagePile> getPileDeplacement() {return PileDeplacement;}
	public void Avance(int modif) {						
			MessagePile mess = new MessagePile("A",modif);
			if(PileDeplacement.isEmpty())
			{					
				PileDeplacement.add(mess);
				t = new Thread(this);
				t.start();												
			}
			else {PileDeplacement.add(mess);}
			repaint();
	}
	
	public void Recule(int modif) {
			MessagePile mess = new MessagePile("R",-modif);
			if(PileDeplacement.isEmpty())
			{					
				PileDeplacement.add(mess);
				t = new Thread(this);
				t.start();												
			}
			else {PileDeplacement.add(mess);}
			repaint();												
	}
	
	private int vitesse = 20;
	public void run() {		
		while(!PileDeplacement.isEmpty())
		{				
			MessagePile mess = PileDeplacement.firstElement();
			if (mess.getCode()=="DI") //droite (integer)
			{
				System.out.println("A droite de "+mess.getObject()); /////////////

				double modifier = getOrientation();
				double angle = java.lang.Math.toRadians((Integer)mess.getObject()) + modifier;
				setOrientation(angle%(java.lang.Math.PI*2));
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if (mess.getCode()=="DD") //droite (double)
			{
				System.out.println("A droite de "+mess.getObject()); /////////////

				double modifier = getOrientation();
				double angle = java.lang.Math.toRadians((Double)mess.getObject()) + modifier;
				setOrientation(angle%(java.lang.Math.PI*2));
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if (mess.getCode()=="GI") //gauche (integer)
			{
				System.out.println("A gauche de "+mess.getObject()); /////////////

				double modifier = getOrientation();
				double angle = modifier - java.lang.Math.toRadians((Integer)mess.getObject());
				setOrientation(angle%(java.lang.Math.PI*2));
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if (mess.getCode()=="GD") //gauche (double)
			{
				System.out.println("A gauche de "+mess.getObject()); /////////////

				double modifier = getOrientation();
				double angle = modifier - java.lang.Math.toRadians((Double)mess.getObject());
				setOrientation(angle%(java.lang.Math.PI*2));
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if(mess.getCode() == "P") //point (integer)
			{
				pointThread(mess);
			}
			else if(mess.getCode() == "N") //nettoie
			{
				System.out.println("Nettoie"); /////////////
				clearScreen();
				repaint();				
			}
			else if(mess.getCode() == "LC") //levecrayon
			{
				System.out.println("levecrayon"); /////////////
				setDessine(false);
			}
			else if(mess.getCode() == "BC") //baissecrayon
			{
				System.out.println("baissecrayon"); /////////////
				setDessine(true);
			}
			else if(mess.getCode() == "FP") //fixecap
			{
				int [] coordonne = (int[]) mess.getObject();		
				System.out.println("fixecap [ "+coordonne[0]+" "+coordonne[1]+" ]"); /////////////
				p1 = new Point(coordonne[0],coordonne[1]);
				repaint();
				setPoint(p1);
				setTortue(p1,orientation,getAffiche());
				ppx = coordonne[0];
				ppy = coordonne[1];
			}
			else if(mess.getCode() == "FCI") //fixecap (integer)
			{
				System.out.println("fixecap"); /////////////
				double angle = java.lang.Math.toRadians((Integer)mess.getObject());
				setOrientation(angle);
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if(mess.getCode() == "FCD") //fixecap (double)
			{
				System.out.println("fixecap"); /////////////
				double angle = java.lang.Math.toRadians((Double)mess.getObject());
				setOrientation(angle);
				setTortue(getPoint(),getOrientation(),getAffiche());
			}
			else if (mess.getCode()=="FCC") //fixecouleurcrayon
			{
				System.out.println("fixe couleur crayon"); /////////////
				setCouleur((Color)mess.getObject());
			}
			else if (mess.getCode()=="FCFG") //fixecouleurfond
			{
				System.out.println("fixe couleur fond"); /////////////
				setFond((Color)mess.getObject());
			}
			else if (mess.getCode()=="E") //epaisseur
			{
				System.out.println("epaisseur"); /////////////
				setEpaisseur((Integer)mess.getObject());
			}
			else if (mess.getCode()=="A" || mess.getCode()=="R") //avance ou recule
			{
				deplacementThread(mess);
			}
			else {System.out.println(mess.getCode());}
			PileDeplacement.removeElementAt(0);
			if (PileDeplacement.isEmpty())
				{
					t.stop();
				}
		}						
	}
	
	private void pointThread(MessagePile mess)
	{
		int [] coordonne = (int[]) mess.getObject();		
		p1 = new Point(coordonne[0],coordonne[1]);
		System.out.println("Point [ "+ppx+" "+ppy+" ]"); /////////////
		if (getDessine())
		{															
			ajouterDessinable(new point(p1,couleur,epaisseur));
			repaint();
		}
		else
		{
			//console.sys("NOTE : Le crayon est leve, aucun point ne sera dessine");		
		}
		setPoint(p1);
		setTortue(p1,orientation,getAffiche());			
		ppx = coordonne[0];
		ppy = coordonne[1];
	}
	private void deplacementThread(MessagePile mess)
	{		
		int sleepTime=100;
		if (vitesse==500) sleepTime=1;
		else if (vitesse<500 && vitesse>=300) sleepTime=30;
		int modifier;
		if ((Integer) mess.getObject()>=0) modifier = (Integer) mess.getObject();
		else  modifier = -(Integer) mess.getObject();
		p1 = new Point();			
		p1 = getPoint();
		Point p1save = getPoint();
		orientation = getOrientation();
		
		System.out.println("Deplacement de "+modifier); ////////////
		//calcul du sens
		boolean sens = (Integer) mess.getObject()>0;
		
		if (getDessine())
		{
		//calcul de la vitesse
		int vitesseAnim = vitesse;
		int i=0;		
		if (vitesseAnim >= modifier) {vitesseAnim=modifier;}
		i = vitesseAnim; 
		changeVitesse = false;
		
		while (i <= modifier)
		{	
			p2 = Calcul(vitesseAnim,orientation,p1,sens);	
			ajouterDessinable(new Ligne(p1,p2,couleur,epaisseur,1));
			setTortue(p2,orientation,getAffiche());					
			repaint();
			try {
				t.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
			i+=vitesseAnim;
//				//recalcul de la vitesse -> en commentaire car probleme lors du recalcul du deplacement
//				if (changeVitesse)
//				{
//					System.out.println("1.vitesse:"+vitesse+" vitesseAnim:"+vitesseAnim+" modifier:"+modifier+" i:"+i);
//					vitesseAnim = vitesse;
//					System.out.println("2.vitesse:"+vitesse+" vitesseAnim:"+vitesseAnim+" modifier:"+modifier+" i:"+i);
//					if (vitesseAnim >= modifier-i) {vitesseAnim=modifier-i;}
//					System.out.println("3.vitesse:"+vitesse+" vitesseAnim:"+vitesseAnim+" modifier:"+modifier+" i:"+i);
//					changeVitesse = false;
//				}				
		}
//			//pb dans calcul avec ppx et ppy
//			p2 = Calcul(modifier,orientation,p1save,sens);
//			ajouterDessinable(new Ligne(p1save,p2,couleur,epaisseur,1));
//			setTortue(p2,orientation,getAffiche());					
		repaint();
		setPoint(p2);
		}
		else
		{
			p1 = new Point();
			p1 = getPoint();
			orientation = getOrientation();
			p2 = Calcul(modifier,orientation,p1,sens);
			setTortue(p2,orientation,getAffiche());
			setPoint(p2);		
		}		
	}
	/********FIN FONCTION DE DEPLACEMENT********/		

	public void StopDeplacement() 
	{
		if(enDeplacement()) 	
			{t.stop();PileDeplacement.removeAllElements();}
	}
	public void PauseDeplacement() 
	{
		if(enDeplacement()) 	
			{
			if(t.isAlive())
				try {
					t.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
	}
	public void FinPauseDeplacement() 
	{
		if(enDeplacement()) 	
			{
				if(t.isAlive()) t.notifyAll();
			}
	}
	
}